package com.engine.jucailinkq.attendance.attendanceanalysis.wrapper;

import com.alibaba.fastjson.JSONObject;
import com.engine.jucailinkq.attendance.attendanceanalysis.dto.clockpoint.ClockPointDTO;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.ComprehensiveWorkingHourService;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.UtilService;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.impl.ComprehensiveWorkingHourServiceImpl;
import com.engine.jucailinkq.attendance.attendanceanalysis.service.impl.UtilServiceImpl;
import com.engine.jucailinkq.attendance.enums.CheckBoxEnum;
import com.engine.jucailinkq.attendance.enums.ClassSegmentTypeEnum;
import com.engine.jucailinkq.common.util.CommonUtil;
import com.engine.jucailinkq.common.util.DateUtil;
import com.engine.common.util.ServiceUtil;
import com.engine.jucailinkq.common.util.Utils;
import com.engine.core.impl.Service;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import weaver.general.BaseBean;
import weaver.general.Util;

import java.time.ZoneOffset;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 考勤分析主题逻辑
 */
@Slf4j
public class AttendanceAnalysisWrapper extends Service {

    private UtilService utilService = ServiceUtil.getService(UtilServiceImpl.class);
    private UpdateAttendanceResultWrapper updateAttendanceResultWrapper = ServiceUtil.getService(UpdateAttendanceResultWrapper.class);
    private ComprehensiveWorkingHourService comprehensiveWorkingHourService = ServiceUtil.getService(ComprehensiveWorkingHourServiceImpl.class);

    /**
     * 考勤分析主逻辑入口
     *
     * @param userId          人员id
     * @param dataList        打卡数据
     * @param attendanceItems 考勤项目
     */
    public void attendanceAnalysis(String userId, List<String> analysisDateList, List<Map<String, Object>> dataList, List<Map<String, Object>> attendanceItems, List<Map<String, Object>> workHourItems, Map<String, Map<String, Object>> clockInTimeMap, Map<String, List<Map<String, Object>>> attendaceResultMap) {
        log.debug("***********analysis userId:{} analysisDate:{} start***********", userId,analysisDateList);
        log.debug("clockInTimeDate : [{}]", dataList);

        String beforeTwoDayDateDate = DateUtil.beforeDay(analysisDateList.get(0), 2);
        /** 打卡数据 */
        Map<String, List<Map<String, Object>>> collect = Maps.newHashMap();
        if (dataList != null && dataList.size() > 0) {
            collect = dataList.stream().collect(Collectors.groupingBy(e -> Util.null2String(e.get("signdate"))));
        }
        /** 获取人员班次*/
        Map<String, Object> classesParamMap = Maps.newHashMap();
        classesParamMap.put("tableName", "uf_pbjg");
        classesParamMap.put("startDate", beforeTwoDayDateDate);
        classesParamMap.put("endDate", DateUtil.AfterDay(analysisDateList.get(analysisDateList.size()-1),1));
        classesParamMap.put("pblx", "0");
        classesParamMap.put("pbdx", userId);
        classesParamMap.put("current", "1");
        classesParamMap.put("pageSize", 9999);
        classesParamMap.put("recurrence", 1);
        classesParamMap.put("clockInTimeMap", clockInTimeMap);
        classesParamMap.put("attendanceItems", attendanceItems);
        classesParamMap.put("clockInData", dataList);
        Map<String, Object> schedulMap = utilService.getSchedulingInFormation(classesParamMap);
        Map<String, List<Map<String, Object>>> schedulingResultsMap = (Map<String, List<Map<String, Object>>>) schedulMap.get("schedulingResultsMap");
        for (String analysisDate :analysisDateList){
            Map<String, Object> getClockTimeParam = Maps.newHashMap();
            getClockTimeParam.put("date", analysisDate);
            getClockTimeParam.put("clockInTimeList", dataList);
            getClockTimeParam.put("clockInTimeCollect", collect);
            getClockTimeParam.put("schedulingResultCollect", schedulingResultsMap);
            getClockTimeParam.put("clockInTimeMap", clockInTimeMap);
            getClockTimeParam.put("userId", userId);

            List<Map<String, Object>> analysisDateAttendaceResult = attendaceResultMap.get(analysisDate);

            if (analysisDateAttendaceResult == null || analysisDateAttendaceResult.size() == 0 || (!CheckBoxEnum.CHECKED.getKey().equals(analysisDateAttendaceResult.get(0).get("sgsj")) &&
                    !"3".equals(analysisDateAttendaceResult.get(0).get("sjzt")))) {
                //打卡数据
                List<Map<String, Object>> analysisDateClockInTimeList = utilService.getClockInTime(getClockTimeParam);
                //分析考勤
                Map<String, Object> recordDataTime1 = analysis(userId, analysisDate, analysisDateClockInTimeList, schedulingResultsMap.get(analysisDate), attendanceItems, workHourItems,(Map<String,Object>)schedulMap.get("clockInTimeDataMap"));
                clockInTimeMap.put(analysisDate, recordDataTime1);
            }
        }
    }

    /**
     * 考勤分析主逻辑入口
     *
     * @param userId          人员id
     * @param analysisDate    分析日期
     * @param dataList        需要包含分析日期以及分析日期前后2天的3天打卡数据
     * @param attendanceItems 人员考勤项目
     */
    public void attendanceAnalysisForApi(String userId, String analysisDate, List<Map<String, Object>> dataList, List<Map<String, Object>> attendanceItems, Map<String, Object> schedulMap, List<Map<String, Object>> workHourItems, Map<String, Map<String, Object>> clockInTimeMap,List<Map<String,Object>> attendaceResult) {
        log.debug("***********analysis userId:{},analysisDate:{} start***********", userId, analysisDate);
        log.debug("clockInTimeDate : [{}]", dataList);
        log.debug("attendaceResult : [{}]",attendaceResult);
        if (attendaceResult!=null && attendaceResult.size()>0 && (CheckBoxEnum.CHECKED.getKey().equals(attendaceResult.get(0).get("sgsj")) ||
                "3".equals(attendaceResult.get(0).get("sjzt")))) {
            return;
        }
        Map<String, List<Map<String, Object>>> collect = Maps.newHashMap();
        if (dataList != null && dataList.size() > 0) {
            collect = dataList.stream().collect(Collectors.groupingBy(e -> Util.null2String(e.get("signdate"))));
        }
        Map<String, List<Map<String, Object>>> schedulingResultsMap = (Map<String, List<Map<String, Object>>>) schedulMap.get("schedulingResultsMap");

        Map<String, Object> getClockTimeParam = Maps.newHashMap();
        getClockTimeParam.put("date", analysisDate);
        getClockTimeParam.put("clockInTimeList", dataList);
        getClockTimeParam.put("schedulingResultCollect", schedulingResultsMap);
        getClockTimeParam.put("clockInTimeMap", clockInTimeMap);
        getClockTimeParam.put("clockInTimeCollect", collect);
        getClockTimeParam.put("userId", userId);
        getClockTimeParam.put("workHourItems",workHourItems);
        //打卡数据
        List<Map<String, Object>> clockInTimeList = utilService.getClockInTime(getClockTimeParam);
        Map<String, Object> recordDataTime = analysis(userId, analysisDate, clockInTimeList, schedulingResultsMap.get(analysisDate), attendanceItems, workHourItems,(Map<String,Object>)schedulMap.get("clockInTimeDataMap"));
        clockInTimeMap.put(analysisDate, recordDataTime);
    }

    /**
     * 分析数据
     *
     * @param userId          分析人员
     * @param analysisDate    分析日期
     * @param clockInTimeList 打卡数据
     * @param scheduleResult  班次
     * @param attendanceItems 考勤项目
     */
    public Map<String, Object> analysis(String userId, String analysisDate, List<Map<String, Object>> clockInTimeList, List<Map<String, Object>> scheduleResult, List<Map<String, Object>> attendanceItems, List<Map<String, Object>> workHourItems
    ,Map<String,Object> clockInTimeDataMap) {
        Map<String, Object> recordParam = Maps.newHashMap();
        Map<String, String> formModeIdMap = Utils.getFormmodeIdMap();
        recordParam.put("userId", userId);
        recordParam.put("analysisDate", analysisDate);
        recordParam.put("classInfo", scheduleResult);
        recordParam.put("formmodeIdMap", formModeIdMap);
        recordParam.put("attendanceDuration", 0);

        recordParam.put("modeId", formModeIdMap.get("uf_jcl_kq_cqjg"));

        Map<String, Object> workingHourparam = Maps.newHashMap();
        workingHourparam.put("clockInTimeCollect", clockInTimeList);
        workingHourparam.put("analysisDate", analysisDate);
        workingHourparam.put("userId", userId);
        workingHourparam.put("attendanceItems", attendanceItems);
        workingHourparam.put("scheduleResult", scheduleResult);

        /**
         * 津贴
         */
        Map<String, Object> allowanceMap = updateAttendanceResultWrapper.allowanceHandle(recordParam);

        BaseBean baseBean = new BaseBean();
        if (CollectionUtils.isEmpty(allowanceMap)) {
            baseBean.writeLog("allowanceMap is null.");
        } else {
            baseBean.writeLog("allowanceMap is:" + JSONObject.toJSONString(allowanceMap));
        }

        if (workHourItems != null && workHourItems.size() > 0) {
            workingHourparam.put("workHourItem", workHourItems.get(0));
            String ifPriority = Util.null2String(workHourItems.get(0).get("hlpbyxsyzhgs"));
            //核算工时的日期类型
            String hsgsdrqlx = Util.null2String(workHourItems.get(0).get("hsgsdrqlx"));
            String rqlx = Utils.getDateType(analysisDate, Util.null2String(workHourItems.get(0).get("qyrl")));

            if (CheckBoxEnum.CHECKED.getKey().equals(ifPriority) && CommonUtil.ifContainStr(hsgsdrqlx, rqlx, ",")) {
                //综合工时
                return (Map<String,Object>)comprehensiveWorkingHourService.excuteByWorkHour(workingHourparam).get("recordDataTime");
            }
        }

        if (scheduleResult == null || scheduleResult.size() == 0) {
            //没有排班
            Map<String, Object> params = Maps.newHashMap();
            params.put("userId", userId);
            params.put("analysisDate", analysisDate);
            params.put("clockInTimeCollect", clockInTimeList);
            params.put("modeId", formModeIdMap.get("uf_jcl_kq_cqjg"));
            params.put("workHourItems", workHourItems);
            params.put("attendanceItems", attendanceItems);
            params.put("allowanceMap", allowanceMap);

            return (Map<String,Object>)updateAttendanceResultWrapper.recordNoClass(params).get("recordDataTime");
        }
        recordParam.put("rqlx", scheduleResult.get(0).get("rqlx"));
        //休息班次,休息班打卡自动加班不算自动加班,扣除原有的工作时段
        if (scheduleResult.get(0).get("sfxx") != null && CheckBoxEnum.CHECKED.getKey().equals(scheduleResult.get(0).get("sfxx")) &&
                scheduleResult.get(0).get("xxbdkzdjb") != null && CheckBoxEnum.UNCHECKED.getKey().equals(scheduleResult.get(0).get("xxbdkzdjb"))) {
            List<Map<String,Object>> overTimePlanList = scheduleResult.stream().filter(e->e.get("bdlx").equals(ClassSegmentTypeEnum.OVERTIME_PLAN.getKey())).collect(Collectors.toList());
            if (overTimePlanList.size() >0){
                scheduleResult = scheduleResult.stream().filter(e->ClassSegmentTypeEnum.OVERTIME_PLAN.getKey().equals(e.get("bdlx")) || Utils.ifRestClassSegment(e.get("bdlx").toString()) || Utils.ifAskforOrEvctionClassSegment(e.get("bdlx").toString())).collect(Collectors.toList());
            }else {
                //没有加班计划，直接记为休息
                updateAttendanceResultWrapper.recordRest(recordParam);
                return null;
            }
        }


        List<Map<String, Object>> askForLeaveAndEvctionSchedule = Lists.newArrayList();
        //请假记录
        Map<String, Object> askForLeaveDataMap = updateAttendanceResultWrapper.getAskForLeave(workingHourparam);
        if (askForLeaveDataMap.get("alldayVaction") != null) {
            //全天请假
            recordParam.put("cqzt", CheckBoxEnum.UNCHECKED.getKey());
            recordParam.put("recordData", askForLeaveDataMap.get("alldayVaction"));
            utilService.recordItem(recordParam);
            return null;
        }
        askForLeaveAndEvctionSchedule.addAll((List<Map<String, Object>>) askForLeaveDataMap.get("askForLeaveSchedule"));

        //出差记录
        Map<String, Object> evectionDataMap = updateAttendanceResultWrapper.getEvection(workingHourparam);
        if (evectionDataMap.get("alldayEvection") != null) {
            //全天出差
            recordParam.put("cqzt", CheckBoxEnum.UNCHECKED.getKey());
            recordParam.put("recordData", evectionDataMap.get("alldayEvection"));
            utilService.recordItem(recordParam);
            return null;
        }
        askForLeaveAndEvctionSchedule.addAll((List<Map<String, Object>>) evectionDataMap.get("evectionSchedule"));


        if (askForLeaveAndEvctionSchedule.size() > 0) {
            askForLeaveAndEvctionSchedule = mergeAskForLeaveAndEvctionSchedule(askForLeaveAndEvctionSchedule, analysisDate);
        }
        Map<String, Object> clcokInTimeDataParam = Maps.newHashMap();
        clcokInTimeDataParam.put("analysisDate", analysisDate);
        clcokInTimeDataParam.put("scheduleResult", scheduleResult);
        clcokInTimeDataParam.put("clockInTimeList", clockInTimeList);
        clcokInTimeDataParam.put("askForLeaveAndEvctionSchedule", askForLeaveAndEvctionSchedule);

        //打卡卡点以及匹配的打卡时间
        List<ClockPointDTO> clcokInTimeData = null;
        if (clockInTimeDataMap.get(analysisDate) != null){
            clcokInTimeData = (List<ClockPointDTO>)clockInTimeDataMap.get(analysisDate);
        }else {
            clcokInTimeData = utilService.getClockInPointCmd(clcokInTimeDataParam);
        }

        baseBean.writeLog("analysis clcokInTimeData:" + JSONObject.toJSONString(clcokInTimeData));

        Map<String, Object> recordAbnormalParam = Maps.newHashMap();
        recordAbnormalParam.put("clcokInTimeData", clcokInTimeData);
        recordAbnormalParam.put("attendanceItems", attendanceItems);
        recordAbnormalParam.put("scheduleResult", scheduleResult);
        recordAbnormalParam.put("analysisDate", analysisDate);
        recordAbnormalParam.put("userId", userId);
        recordAbnormalParam.put("rqlx", scheduleResult.get(0).get("rqlx"));

        recordAbnormalParam.put("formmodeIdMap", formModeIdMap);




        /**
         * 计算是否考勤异常,是否有早退、迟到、漏卡的情况
         *
         **/

        recordAbnormalParam.put("askForLeaveList", askForLeaveDataMap.get("askForLeaveList"));
        recordAbnormalParam.put("askForLeaveItems", askForLeaveDataMap.get("askForLeaveItems"));
        recordAbnormalParam.put("evectionList", evectionDataMap.get("evectionList"));
        recordAbnormalParam.put("evectionItems", evectionDataMap.get("evectionItems"));

        List<Map<String, Object>> abnormalClockInList = updateAttendanceResultWrapper.recordAbnormalClockIn(recordAbnormalParam);

        /**
         * 出勤津贴
         */
        recordAbnormalParam.put("abnormalClockInList", abnormalClockInList);
        //recordAbnormalParam.put("notRecordAbnormalClockInList", notRecordAbnormalClockInList);
        String ifnotWork = Util.null2String(allowanceMap.get("ifnotWork"));
        if (!"".equals(ifnotWork)) {
            //次日免班
            recordParam.put("recordData", Lists.newArrayList());
            recordParam.put("recordDataTime", Maps.newHashMap());
            recordParam.put("attendanceDuration", scheduleResult.get(0).get("edsc").toString());
            recordParam.put("cqzt", CheckBoxEnum.UNCHECKED.getKey());
            utilService.recordItem(recordParam);
            return null;
        }
        if (!"".equals(Util.null2String(allowanceMap.get("delayMinute")))) {
            recordAbnormalParam.put("delayMinute", allowanceMap.get("delayMinute"));
            updateAttendanceResultWrapper.removeAbnormal(recordAbnormalParam);
        }

        /**
         * 请假
         */
        Map<String, Object> vactionMap = updateAttendanceResultWrapper.recordAskForLeave(recordAbnormalParam);

        if (CollectionUtils.isEmpty(vactionMap)) {
            baseBean.writeLog("vactionMap is null.");
        } else {
            baseBean.writeLog("vactionMap is:" + JSONObject.toJSONString(vactionMap));
        }

        List<Map<String, Object>> abnormalClockInListByAskForLeave = (List<Map<String, Object>>) vactionMap.get("abnormalClockInList");
        List<Map<String, Object>> vactionList = (List<Map<String, Object>>) vactionMap.get("resultList");
        recordAbnormalParam.put("offsetAskForLeaveAnomaly", vactionMap.get("offsetAskForLeaveAnomaly"));
        recordAbnormalParam.put("askForLeaveToOffsetAbnomaly",vactionMap.get("askForLeaveToOffsetAbnomaly"));
        log.debug("请假后消除的异常 : [{}]", vactionMap.get("offsetAskForLeaveAnomaly"));
        /**
         * 出差、外出
         */
        recordAbnormalParam.put("abnormalClockInList", abnormalClockInListByAskForLeave);

        Map<String, Object> recordEvection = updateAttendanceResultWrapper.recordEvection(recordAbnormalParam);

        if (CollectionUtils.isEmpty(recordEvection)) {
            baseBean.writeLog("recordEvection is null.");
        } else {
            baseBean.writeLog("recordEvection is:" + JSONObject.toJSONString(recordEvection));
        }

        List<Map<String, Object>> abnormalClockInListByEvction = (List<Map<String, Object>>) recordEvection.get("abnormalClockInList");
        List<Map<String, Object>> evectionResultList = (List<Map<String, Object>>) recordEvection.get("resultList");

        recordAbnormalParam.put("abnormalClockInList", abnormalClockInListByEvction);
        recordAbnormalParam.put("offsetEvectionAnomaly", recordEvection.get("offsetEvectionAnomaly"));
        recordAbnormalParam.put("evectionToOffsetAbnomaly",recordEvection.get("evectionToOffsetAbnomaly"));
        log.debug("出差外出后消除的异常 : [{}]", recordEvection.get("offsetEvectionAnomaly"));
        /**
         * 加班
         * workOverTimeItemList:加班项目集合
         * overTimeHolidayList：假期余额集合
         * overTimeResultList：加班结果余额
         */
        Map<String, Object> recordWorkOverTime = updateAttendanceResultWrapper.recordWorkOverTime(recordAbnormalParam);
        if (CollectionUtils.isEmpty(recordWorkOverTime)) {
            baseBean.writeLog("recordWorkOverTime is null.");
        } else {
            baseBean.writeLog("recordWorkOverTime is:" + JSONObject.toJSONString(recordWorkOverTime));
        }

        baseBean.writeLog("recordAbnormalParam is:" + JSONObject.toJSONString(recordAbnormalParam));

        /**
         * 计算出勤时长
         */
        double attendanceDuration = utilService.computeAttendanceDuration(recordAbnormalParam);

        /**
         * 入库
         */
        List<Map<String, Object>> recordData = Lists.newArrayList();
        recordData.addAll(vactionList);
        recordData.addAll(evectionResultList);
        abnormalClockInListByEvction = abnormalClockInListByEvction.stream().filter(e -> (boolean) e.get("record")).collect(Collectors.toList());
        recordData.addAll(abnormalClockInListByEvction);
        List<Map<String, Object>> workOverTimeItemList = (List<Map<String, Object>>)recordWorkOverTime.get("workOverTimeItemList");
        if (workOverTimeItemList.size() > 0){
            recordData.addAll(workOverTimeItemList);
        }
        recordParam.put("recordData", recordData);
        recordParam.put("recordDataTime", utilService.getNeedRecordClockInTime(clcokInTimeData));
        recordParam.put("attendanceDuration", attendanceDuration);
        recordParam.put("overTimeHolidayList",recordWorkOverTime.get("overTimeHolidayList") == null?Lists.newArrayList():recordWorkOverTime.get("overTimeHolidayList"));
        recordParam.put("overTimeResultList",recordWorkOverTime.get("overTimeResultList") == null?Lists.newArrayList():recordWorkOverTime.get("overTimeResultList"));
        log.debug("recordParam : {}", recordParam);
        if (abnormalClockInListByEvction.size() > 0) {
            recordParam.put("cqzt", CheckBoxEnum.CHECKED.getKey());
        } else {
            recordParam.put("cqzt", CheckBoxEnum.UNCHECKED.getKey());
        }

        //休息班次,休息班打卡自动加班不算自动加班,且为正常
        if (scheduleResult.get(0).get("sfxx") != null && CheckBoxEnum.CHECKED.getKey().equals(scheduleResult.get(0).get("sfxx"))) {
            recordParam.put("sfxx", CheckBoxEnum.CHECKED.getKey());
            recordParam.put("cqzt", CheckBoxEnum.UNCHECKED.getKey());
            recordParam.put("attendanceDuration", 0);
        }
        baseBean.writeLog("analysis recordParam:" + JSONObject.toJSONString(recordParam));
        utilService.recordItem(recordParam);
        return (Map<String, Object>) recordParam.get("recordDataTime");
    }


    /**
     * 合并请假或出差
     *
     * @param askForLeaveAndEvctionSchedule
     * @param analysisDate
     * @return
     */
    public List<Map<String, Object>> mergeAskForLeaveAndEvctionSchedule(List<Map<String, Object>> askForLeaveAndEvctionSchedule, String analysisDate) {

        askForLeaveAndEvctionSchedule = askForLeaveAndEvctionSchedule.stream().sorted(Comparator.comparing(e -> DateUtil.getTime(Utils.getkssjTime(e, analysisDate)).toInstant(ZoneOffset.of("+8")).toEpochMilli())).collect(Collectors.toList());

        for (int i = 0; i < askForLeaveAndEvctionSchedule.size() - 1; i++) {
            String beforeDtkssj = Utils.getkssjTime(askForLeaveAndEvctionSchedule.get(i),analysisDate);
            String beforeDtjssj = Utils.getjssjTime(askForLeaveAndEvctionSchedule.get(i),analysisDate);

            for (int j = i + 1; j < askForLeaveAndEvctionSchedule.size(); j++) {
                String dtkssj = Utils.getkssjTime(askForLeaveAndEvctionSchedule.get(j),analysisDate);
                String dtjssj = Utils.getjssjTime(askForLeaveAndEvctionSchedule.get(j),analysisDate);
                if (DateUtil.getTime(beforeDtjssj).compareTo(DateUtil.getTime(dtkssj)) == 0) {
                    askForLeaveAndEvctionSchedule.get(i).put("dtjssj", dtjssj);
                    askForLeaveAndEvctionSchedule.get(i).put("jsdk", askForLeaveAndEvctionSchedule.get(j).get("jsdk"));
                    askForLeaveAndEvctionSchedule.get(i).put("thfghlfzs", askForLeaveAndEvctionSchedule.get(j).get("thfghlfzs"));
                    askForLeaveAndEvctionSchedule.remove(askForLeaveAndEvctionSchedule.get(j));
                    j--;
                    i--;
                }
            }
        }

        return askForLeaveAndEvctionSchedule;
    }
}
